"""
健康检查服务 - 监控系统状态和性能
"""
import asyncio
import aiohttp
import logging
from typing import Dict, List, Any, Optional
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy import and_

from app.models.release import HealthCheck, Release
from app.core.config import current_settings

logger = logging.getLogger(__name__)


class HealthCheckService:
    """健康检查服务类"""
    
    def __init__(self, db: Session):
        self.db = db
        self.session = None
    
    async def perform_health_checks(self, release_id: int) -> Dict[str, Any]:
        """执行健康检查"""
        try:
            release = self.db.query(Release).filter(Release.id == release_id).first()
            if not release:
                raise ValueError(f"发布记录不存在: {release_id}")
            
            # 获取健康检查配置
            health_checks = self._get_health_check_configs(release)
            
            # 执行检查
            results = []
            async with aiohttp.ClientSession() as session:
                self.session = session
                for check_config in health_checks:
                    result = await self._execute_health_check(check_config)
                    results.append(result)
                    
                    # 保存检查结果
                    await self._save_health_check_result(release_id, result)
            
            # 统计结果
            total_checks = len(results)
            passed_checks = sum(1 for r in results if r["status"])
            failed_checks = total_checks - passed_checks
            
            return {
                "success": failed_checks == 0,
                "total_checks": total_checks,
                "passed_checks": passed_checks,
                "failed_checks": failed_checks,
                "results": results
            }
            
        except Exception as e:
            logger.error(f"健康检查执行失败: {e}")
            return {
                "success": False,
                "error": str(e),
                "total_checks": 0,
                "passed_checks": 0,
                "failed_checks": 0,
                "results": []
            }
    
    async def continuous_health_monitoring(self, release_id: int, interval: int = 30):
        """持续健康监控"""
        try:
            while True:
                await self.perform_health_checks(release_id)
                await asyncio.sleep(interval)
        except asyncio.CancelledError:
            logger.info(f"健康监控已停止: {release_id}")
        except Exception as e:
            logger.error(f"健康监控异常: {e}")
    
    async def _execute_health_check(self, check_config: Dict[str, Any]) -> Dict[str, Any]:
        """执行单个健康检查"""
        check_type = check_config.get("type", "http")
        check_name = check_config.get("name", "unknown")
        
        try:
            if check_type == "http":
                result = await self._http_health_check(check_config)
            elif check_type == "tcp":
                result = await self._tcp_health_check(check_config)
            elif check_type == "command":
                result = await self._command_health_check(check_config)
            else:
                result = {
                    "name": check_name,
                    "type": check_type,
                    "status": False,
                    "error": f"不支持的检查类型: {check_type}"
                }
            
            return result
            
        except Exception as e:
            logger.error(f"健康检查执行失败 {check_name}: {e}")
            return {
                "name": check_name,
                "type": check_type,
                "status": False,
                "error": str(e),
                "response_time": 0
            }
    
    async def _http_health_check(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """HTTP健康检查"""
        url = config.get("url")
        method = config.get("method", "GET")
        timeout = config.get("timeout", 30)
        expected_status = config.get("expected_status", 200)
        headers = config.get("headers", {})
        
        start_time = datetime.now()
        
        try:
            async with self.session.request(
                method, url, headers=headers, timeout=timeout
            ) as response:
                response_time = (datetime.now() - start_time).total_seconds() * 1000
                
                # 检查状态码
                status_ok = response.status == expected_status
                
                # 检查响应内容（如果配置了）
                content_ok = True
                if config.get("expected_content"):
                    content = await response.text()
                    content_ok = config["expected_content"] in content
                
                # 检查响应时间
                time_ok = response_time <= config.get("max_response_time", 5000)
                
                overall_status = status_ok and content_ok and time_ok
                
                return {
                    "name": config.get("name", "HTTP检查"),
                    "type": "http",
                    "url": url,
                    "method": method,
                    "status": overall_status,
                    "status_code": response.status,
                    "response_time": int(response_time),
                    "expected_status": expected_status,
                    "content_match": content_ok,
                    "time_ok": time_ok
                }
                
        except asyncio.TimeoutError:
            return {
                "name": config.get("name", "HTTP检查"),
                "type": "http",
                "url": url,
                "status": False,
                "error": "请求超时",
                "response_time": config.get("timeout", 30) * 1000
            }
        except Exception as e:
            return {
                "name": config.get("name", "HTTP检查"),
                "type": "http",
                "url": url,
                "status": False,
                "error": str(e),
                "response_time": 0
            }
    
    async def _tcp_health_check(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """TCP健康检查"""
        host = config.get("host")
        port = config.get("port")
        timeout = config.get("timeout", 10)
        
        start_time = datetime.now()
        
        try:
            # 创建TCP连接
            reader, writer = await asyncio.wait_for(
                asyncio.open_connection(host, port),
                timeout=timeout
            )
            
            response_time = (datetime.now() - start_time).total_seconds() * 1000
            
            # 关闭连接
            writer.close()
            await writer.wait_closed()
            
            return {
                "name": config.get("name", "TCP检查"),
                "type": "tcp",
                "host": host,
                "port": port,
                "status": True,
                "response_time": int(response_time)
            }
            
        except asyncio.TimeoutError:
            return {
                "name": config.get("name", "TCP检查"),
                "type": "tcp",
                "host": host,
                "port": port,
                "status": False,
                "error": "连接超时",
                "response_time": timeout * 1000
            }
        except Exception as e:
            return {
                "name": config.get("name", "TCP检查"),
                "type": "tcp",
                "host": host,
                "port": port,
                "status": False,
                "error": str(e),
                "response_time": 0
            }
    
    async def _command_health_check(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """命令健康检查"""
        command = config.get("command")
        timeout = config.get("timeout", 30)
        
        start_time = datetime.now()
        
        try:
            # 执行命令
            process = await asyncio.create_subprocess_shell(
                command,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await asyncio.wait_for(
                process.communicate(),
                timeout=timeout
            )
            
            response_time = (datetime.now() - start_time).total_seconds() * 1000
            
            # 检查退出码
            status = process.returncode == config.get("expected_exit_code", 0)
            
            return {
                "name": config.get("name", "命令检查"),
                "type": "command",
                "command": command,
                "status": status,
                "exit_code": process.returncode,
                "stdout": stdout.decode() if stdout else "",
                "stderr": stderr.decode() if stderr else "",
                "response_time": int(response_time)
            }
            
        except asyncio.TimeoutError:
            return {
                "name": config.get("name", "命令检查"),
                "type": "command",
                "command": command,
                "status": False,
                "error": "命令执行超时",
                "response_time": timeout * 1000
            }
        except Exception as e:
            return {
                "name": config.get("name", "命令检查"),
                "type": "command",
                "command": command,
                "status": False,
                "error": str(e),
                "response_time": 0
            }
    
    async def _save_health_check_result(self, release_id: int, result: Dict[str, Any]):
        """保存健康检查结果"""
        try:
            health_check = HealthCheck(
                release_id=release_id,
                check_name=result["name"],
                check_type=result["type"],
                status=result["status"],
                response_time=result.get("response_time", 0),
                status_code=result.get("status_code"),
                response_body=result.get("stdout", ""),
                error_message=result.get("error", ""),
                config=result
            )
            
            self.db.add(health_check)
            self.db.commit()
            
        except Exception as e:
            logger.error(f"保存健康检查结果失败: {e}")
            self.db.rollback()
    
    def _get_health_check_configs(self, release: Release) -> List[Dict[str, Any]]:
        """获取健康检查配置"""
        # 从发布配置中获取健康检查配置
        config = release.config or {}
        health_checks = config.get("health_checks", [])
        
        # 如果没有配置，使用默认配置
        if not health_checks:
            health_checks = self._get_default_health_checks(release.environment)
        
        return health_checks
    
    def _get_default_health_checks(self, environment: str) -> List[Dict[str, Any]]:
        """获取默认健康检查配置"""
        base_url = self._get_base_url(environment)
        
        return [
            {
                "name": "API健康检查",
                "type": "http",
                "url": f"{base_url}/health",
                "method": "GET",
                "expected_status": 200,
                "timeout": 30,
                "max_response_time": 5000
            },
            {
                "name": "数据库连接检查",
                "type": "http",
                "url": f"{base_url}/health/db",
                "method": "GET",
                "expected_status": 200,
                "timeout": 10,
                "max_response_time": 2000
            },
            {
                "name": "Redis连接检查",
                "type": "http",
                "url": f"{base_url}/health/redis",
                "method": "GET",
                "expected_status": 200,
                "timeout": 10,
                "max_response_time": 2000
            }
        ]
    
    def _get_base_url(self, environment: str) -> str:
        """获取基础URL"""
        url_map = {
            "development": "http://localhost:8000",
            "testing": "http://test-api.example.com",
            "staging": "http://staging-api.example.com",
            "production": "https://api.example.com"
        }
        return url_map.get(environment, "http://localhost:8000")
    
    def get_health_check_history(
        self, 
        release_id: int, 
        limit: int = 100
    ) -> List[HealthCheck]:
        """获取健康检查历史"""
        return self.db.query(HealthCheck).filter(
            HealthCheck.release_id == release_id
        ).order_by(HealthCheck.checked_at.desc()).limit(limit).all()
    
    def get_health_check_summary(self, release_id: int) -> Dict[str, Any]:
        """获取健康检查摘要"""
        checks = self.db.query(HealthCheck).filter(
            HealthCheck.release_id == release_id
        ).all()
        
        if not checks:
            return {
                "total_checks": 0,
                "passed_checks": 0,
                "failed_checks": 0,
                "success_rate": 0.0,
                "avg_response_time": 0
            }
        
        total_checks = len(checks)
        passed_checks = sum(1 for c in checks if c.status)
        failed_checks = total_checks - passed_checks
        success_rate = (passed_checks / total_checks) * 100
        
        response_times = [c.response_time for c in checks if c.response_time]
        avg_response_time = sum(response_times) / len(response_times) if response_times else 0
        
        return {
            "total_checks": total_checks,
            "passed_checks": passed_checks,
            "failed_checks": failed_checks,
            "success_rate": round(success_rate, 2),
            "avg_response_time": round(avg_response_time, 2)
        }
